# !/usr/bin/env python
# -*- coding: utf-8 -*-
"""
 -------------------------------------------------
    File Name:     basecase.py
    Description:   
 -------------------------------------------------
 """
import re

import requests

from core.postman import BasePM
# from core.script_executor import ScriptExecutor  # 导入脚本执行器
from core.caselog import CaseRunLog

from hellchin_lib import LoggerManager
from box import Box
from _hellchin_lib.assertion.method_assert import AssertionUndefinedError
from core.DBClient import db_obj

# from core.dbclient import DBClient, db

# from . import testtools as functools

# 定义一个全局变量用来保存测试执行环境的数据
ENV = {}


# 创建一个数据库连接对象
# db_obj = DBClient()


class ScriptExecutor(CaseRunLog):
    """专门用于执行前置和后置脚本"""

    def __init__(self):
        super().__init__()
        self.script_hook = None

    def __run_script(self, data):
        """
        执行脚本
        :param data: 包含前置和后置脚本的用例数据
        :return: 生成器
        """

        pm = BasePM()
        # print =

        globals_cope = {
            # "__builtins__": None, # 屏蔽掉内置函数,
            "pm": pm,
            "Box": Box,
            "db": db_obj,
            "print": self.print_log
        }
        # locals_cope = {}

        # 获取用例的前置脚本
        setup_script = data.get('setup_script')
        # 执行前置脚本
        exec(setup_script, globals_cope)

        # pm = BasePM()
        # 接受传进来的响应结果
        pm.response = yield  # 动态添加response属性

        # test_name = "我又被识别到吗？"
        # globals_cope.update({
        #     "pm": pm
        # })

        # 执行后置脚本
        teardown_script = data.get('teardown_script')
        if teardown_script:
            # exec(teardown_script, globals_cope)

            # 在前后置脚本中捕获断言错误，异常错误等。全部捕捉。与 test 不同。
            try:
                exec(teardown_script, globals_cope)  # locals_cope
                # exec("raise Exception('异常测试')")
            except AssertionUndefinedError as e:
                self.error_log(f"undefined error: ", e.args[0].msg)
                raise Exception(e.args[0].msg)
            except AssertionError as e:
                self.warning_log("硬断言失败: ", e.args[0].msg)
                # print("硬断言失败: ", e.args[0].msg)
                # raise e
            # 捕捉通用异常
            except Exception as e:
                self.error_log("后置脚本错误: \n", e)
                # print("后置脚本错误: \n", e)

        yield

    def run_setup_script(self, data):
        """
        执行前置脚本
        :param data: 包含前置脚本的用例数据
        :return: None
        """
        # 使用脚本执行器函数创建生成器对象
        self.script_hook = self.__run_script(data)

        # 执行前置脚本（启动生成器）
        next(self.script_hook)

    def run_teardown_script(self, response):
        """
        执行后置脚本
        :param response: 接口请求的响应
        :return: None
        """
        # 执行后置脚本
        self.script_hook.send(response)

        # 删除生成器对象
        delattr(self, 'script_hook')


class BaseCase(CaseRunLog):
    """用例执行的基本父类"""

    def __handler_request_data(self, data):
        """
        处理请求数据

        在执行请求前对数据进行处理
        :param data:
        :return:
        """
        request_data = dict()

        # 1. 处理请求的url
        request_data["method"] = data["interface"]["method"].upper()
        url = data["interface"]["url"]
        if not url.startswith("http"):
            url = ENV.get("base_url") + url
        request_data["url"] = url

        # 2. 处理请求的headers
        headers: dict = ENV.get("headers")  # 获取全局headers
        headers.update(data["headers"])  # 合并用例headers与全局headers
        request_data["headers"] = headers  # 返回处理后的请求数据

        # 3. 处理请求的参数和数据
        if "params" in data["request"]:
            request_data["params"] = data["request"]["params"]

        if headers.get("Content-Type") == "application/json":
            # json格式的请求数据
            request_data["json"] = data["request"]["json"]
        elif headers.get("Content-Type") == "application/x-www-form-urlencoded":
            # form-data格式的请求数据
            request_data["data"] = data["request"]["data"]
        elif headers.get("Content-Type") == "multipart/form-data":
            # 文件上传格式的请求数据
            request_data["files"] = data["request"]["files"]

        request_data = self.replace_var(request_data)

        return request_data

    @staticmethod
    def replace_var(var):
        """
        替换用例中的变量
        !!! 注意，这里只是用环境变量替换的。没有做其他的替换处理，后续需要优化 ！！！
        :param var:
        :return:
        """
        pattern = r"\${{(.+?)}}"  # 正则表达式，匹配变量，如：${{username}}
        data = str(var)  # 将变量转换为字符串

        # 循环替换变量
        while re.search(pattern, data):
            match = re.search(pattern, data)  # 匹配变量
            key = match.group(1)  # 获取变量名
            value = ENV.get("Envs").get(key)  # 获取变量值
            data = data.replace(match.group(), str(value))  # 替换变量

        return eval(data)

    def __send_request(self, data):
        """
        发送请求的方法

        :param data:
        :return:
        """
        # 处理请求数据(处理请求的 url 以及 headers、params、data、files)， 并返回处理后的请求数据
        request_info = self.__handler_request_data(data)
        self.info_log("发送[{}]请求: {}".format(request_info['method'].upper(), request_info['url']))

        # try-except 语句块，捕获可能的异常
        try:
            # 发送请求，解包请求数据，发送请求，并返回响应
            response = requests.request(**request_info)
        except Exception as e:
            raise ValueError("请求发送失败，错误信息如下: {}".format(e))
            # print(response.text)
        except requests.exceptions.ConnectionError as e:
            # print(f"连接错误: {e}")
            raise "连接错误：{}".format(e)

        self.url = response.request.url
        self.method = response.request.method
        self.status_cede = response.status_code
        self.response_header = dict(response.headers)
        self.requests_header = dict(response.request.headers)
        self.info_log("请求地址", response.url)
        self.info_log("请求方法", response.request.method)
        # self.info_log("请求头", response.request.headers)

        return response  # 返回响应

    def perform(self, data):
        """
        执行单条用例的入口方法

        :param data: 传入的执行数据
        :return:
        """
        script_executor_obj = ScriptExecutor()  # 实例化脚本执行器

        # self.title = data["title"]

        # 1. 执行前置脚本，并传入用例数据
        script_executor_obj.run_setup_script(data)
        # 2. 发送请求，并返回响应
        response = self.__send_request(data)
        # 3. 传入响应结果，执行后置脚本
        script_executor_obj.run_teardown_script(response)

        return response

